#define MSMD5_C
#include <stdlib.h>  // malloc, free
#include <string.h>  // memcpy, memset
#include <stdio.h>
#include <limits.h>  // ULLONG_MAX
#include <math.h>    // fabs, sin
#include <libmslog/mslog.h>
#include "mscommon.h"

#define FLAG "MSMD5"
#define DEBUG 0


//检测运行环境
static ms_bool msmd5_innerapi_envCheck(ms_u64 len) {
    // 检测 long long 数据类型的大小
    if (sizeof(ms_u64) != 8) {
        ms_errRet(ms_false,"期望ms_u64的长度为 8 个字节，期望没有满足");
    }
    // 检测 int 数据类型的大小
    if (sizeof(ms_u32) != 4) {
        ms_errRet(ms_false,"期望 ms_u32的长度为 4 个字节，期望没有满足");
    }
    // 检测输入信息的长度
    if (len > ULLONG_MAX / 8) {
        ms_errRet(ms_false,"期望输入信息的长度小于等于为 2 的 64 比特，期望没有满足\n");
    }
    // 检测是否满足小端序
    ms_u08 tmp = 0;
    ms_u32 mask = 0x12345678;
    memcpy(&tmp, &mask, 1);
    if (tmp == 0x12) {
        ms_errRet(ms_false,"期望环境为小端序，期望没有满足");
    } else if (tmp != 0x78) {
        ms_errRet(ms_false,"检测端序出错");
    }
    return ms_true;
}

// 计算填充后的信息长度
static ms_u64 msmd5_innerapi_getPadLen(ms_u64 len) 
{
    ms_s32 FillSize = 448 - ((len * 8) % 512);
    if (FillSize <= 0) {
        FillSize += 512;
    }
    ms_u32 FSbyte = (ms_u32)FillSize / 8;
    return (len + 8 + FSbyte);
}

// 32 位数据循环左移
static ms_u32 msmd5_innerapi_leftrotate(ms_u32 src, ms_u08 cx) 
{
    if (cx > 32){
		cx %= 32;
	}
    return ((src << cx) | (src >> (32 - cx)));
}

// MD5 算法
ms_bool msmd5_api_outNum(ms_byte * input,ms_u64 len_input,ms_byte * output) 
{
    if (msmd5_innerapi_envCheck(len_input) != ms_true) {
        return ms_false;
    }
    // ****** 函数初始化 ******
    ms_u32 K[64];
    for (int i = 0; i < 64; ++i) {
        K[i] = (ms_u32)floor((((ms_u64)1) << 32) * fabs(sin(i + 1)));
    }
    const ms_u08 s[4][4] = {
        {7, 12, 17, 22}, {5, 9, 14, 20}, {4, 11, 16, 23}, {6, 10, 15, 21}};

    // ****** 填充 ******
    ms_u64 lenm = msmd5_innerapi_getPadLen(len_input);
    ms_byte *message =ms_null;
	ms_malloc_ret(ms_false, message, (lenm * sizeof(unsigned char)), "message");
    ms_memcpy(message, input, len_input);
    message[len_input] = 0x80;
    ms_u64 lenBit = len_input << 3;  // len_in * 8 表示比特数目
    ms_memcpy(message + lenm - 8, &lenBit, 8);
#if DEBUG
    ms_debug("pading = ");
    for (int i = 0; i < lenm; ++i) {
        ms_debug("%02x", message[i]);
    }
    putchar(10);
#endif

    // ****** 缓冲区初始化 ******
    ms_u32 a0 = 0x67452301;
    ms_u32 b0 = 0xEFCDAB89;
    ms_u32 c0 = 0x98BADCFE;
    ms_u32 d0 = 0x10325476;

    // ****** 循环压缩 ******
    for (ms_s32 k = 0; k < lenm; k += 64) {
        ms_u32 M[16];

        ms_memcpy(&M, message + k, 64);
#if DEBUG
        ms_debug("M = ");
        for (int i = 0; i < 16; ++i) {
            ms_debug("%08x", M[i]);
        }
        putchar(10);
#endif
        ms_u32 A = a0, B = b0, C = c0, D = d0;

        for (int i = 0; i < 64; ++i) {
            ms_u32 F;
            int g;
            if (0 <= i && i < 16) {
                F = (B & C) | ((~B) & D);
                g = i;
            } else if (16 <= i && i < 32) {
                F = (D & B) | ((~D) & C);
                g = (5 * i + 1) % 16;
            } else if (32 <= i && i < 48) {
                F = B ^ C ^ D;
                g = (3 * i + 5) % 16;
            } else if (48 <= i && i < 64) {
                F = C ^ (B | (~D));
                g = (7 * i) % 16;
            }
            F = F + A + K[i] + M[g];
            A = D;
            D = C;
            C = B;
            B = B + msmd5_innerapi_leftrotate(F, s[i / 16][i % 4]);
#if DEBUG
            ms_debug("A = %08x\n", A);
            ms_debug("B = %08x\n", B);
            ms_debug("C = %08x\n", C);
            ms_debug("D = %08x\n", D);
            ms_debug("-----------------------\n");
#endif
        }
        a0 += A;
        b0 += B;
        c0 += C;
        d0 += D;
#if DEBUG
        ms_debug("a = %08x\n", a0);
        ms_debug("b = %08x\n", b0);
        ms_debug("c = %08x\n", c0);
        ms_debug("d = %08x\n", d0);
#endif
    }

    // ****** 得出结果 ******
    ms_memcpy(output, &a0, 4);
    ms_memcpy(output + 4, &b0, 4);
    ms_memcpy(output + 8, &c0, 4);
    ms_memcpy(output + 12, &d0, 4);
	ms_deMalloc(message);
    return ms_true;
}

ms_s32 msmd5_api_outStr(ms_byte * input,ms_u64 len_input,ms_byte * outStr) 
{
	ms_byte outNum[32]={0};
	ms_byte tbuf[1024]={0};
	ms_memset(outStr, 0, 16);
	msmd5_api_outNum(input,len_input,outNum);
    for (int i = 0; i < 16; ++i) {
		ms_strcats(tbuf, outStr,"%02x", (outNum[i]&0xFF));
    }
#if 0
	ms_debug("input(%d):%s",len_input,input);
	ms_debug("outStr:%s",outStr);
#endif
	return 16;
}
/*支持多次加盐MD5算法
inStr：原始字符串
exStr：多个盐字符串
num：MD5次数
outStr：运算结果
*/
void msmd5_api_outStr2(ms_string inStr, ms_string exStr[], ms_s08 num,ms_string outStr)
{
	
	if(num>10){
		ms_waring("Only support 10 time MD5,force set to 10");
		num=10;
	}
	ms_byte bufTem[10][1024]={0};
	ms_byte outTem[1024]={0};
	ms_s08 index=0;
	
	for(index=0;index<num;index++){
		if(0==index){
			ms_strcpy(bufTem[index], inStr);
		}else{
			ms_strcpy(bufTem[index], outTem);
		}
		//加盐
		ms_strcat(bufTem[index], exStr[index]);
		ms_memset0(outTem, ms_bufsize(outTem));
		msmd5_api_outStr(bufTem[index], ms_buflen(bufTem[index]), outTem);
	}
	ms_strcpy(outStr, outTem);
}

#undef MSMD5_C
